using System.Security.Claims;
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Diagnostics;
using oms.Shared.Models;

namespace oms.Shared.Data;

/// <summary>
/// 实体保存拦截器
/// 用于在保存实体时自动设置创建时间、创建人、更新时间、更新人等属性。
/// </summary>
public class EntitySaveInterceptor(IdGenerator idGenerator, IHttpContextAccessor httpContextAccessor)
    : SaveChangesInterceptor
{
    
    /// <summary>
    /// 拦截保存更改事件，自动设置创建时间、创建人、更新时间、更新人等属性。
    /// </summary>
    /// <param name="eventData">数据库上下文事件数据</param>
    /// <param name="result">拦截结果</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>拦截结果</returns>
    public async ValueTask<InterceptionResult<int>> InterceptSaveChanges(
        DbContextEventData eventData,
        InterceptionResult<int> result,
        CancellationToken cancellationToken = default)
    {
        var dbContext = eventData.Context;
        if (dbContext == null) return await base.SavingChangesAsync(eventData, result, cancellationToken);
        var currentUserId = GetCurrentUserId();
        var currentTime = DateTime.Now;

        foreach (var entry in dbContext.ChangeTracker.Entries<BaseEntity>())
        {
            var entity = entry.Entity;
            switch (entry.State)
            {
                case EntityState.Added:
                    entity.Id = await idGenerator.GenerateIdAsync();
                    entity.CreateTime = currentTime;
                    entity.CreateBy = currentUserId;
                    break;
                case EntityState.Modified:
                    entry.Entity.UpdateTime = currentTime;
                    entry.Entity.UpdateBy = currentUserId;
                    break;
            }
        }

        return await base.SavingChangesAsync(eventData, result, cancellationToken);
    }

    /// <summary>
    /// 获取当前用户ID
    /// </summary>
    /// <returns>当前用户ID</returns>
    private string GetCurrentUserId()
    {
        return httpContextAccessor.HttpContext?.User.FindFirstValue(ClaimTypes.NameIdentifier) ?? string.Empty;
    }
}